#![allow(unused)]
#![allow(non_snake_case)]

use core::{
    default,
    ops::{Add, Index, IndexMut, Mul, Sub},
};

// use std::fmt::{Debug, Display};

// Definition of Matrix struct
pub struct Matrix<T, const R: usize, const C: usize> {
    pub raw :[[T; C]; R],
}

macro_rules! mat {
    ($T:ident, $R:expr, $C:expr) => {
        Matrix::<$T, $R, $C>::new()
    };

    ($src:expr) => {
        Matrix::<_, { $src.len() }, { $src[0].len() }>::from_raw(&($src))
    };
}

impl<T :Default + Copy, const R: usize, const C: usize> Matrix<T, R, C> {
    pub fn new() -> Self {
        Self {
            raw :[[T::default(); C]; R],
        }
    }

    pub fn from_raw(src :&[[T; C]; R]) -> Self { Self { raw :*src } }

    pub fn clone(&self) -> Self {
        Self {
            raw :self.raw.clone(),
        }
    }
}

impl<T, const R: usize, const C: usize> Matrix<T, R, C> {
    pub fn get_raw(&self) -> &[[T; C]; R] { &self.raw }

    pub fn get_mut_raw(&mut self) -> &mut [[T; C]; R] { &mut self.raw }

    pub fn row(&self) -> usize { R }

    pub fn col(&self) -> usize { C }

    pub fn at(&self, r :usize, c :usize) -> &T {
        if r < self.row() && c < self.col() {
            &self.raw[r][c]
        } else {
            panic!("Index Out of Bounds.");
        }
    }

    pub fn at_mut(&mut self, r :usize, c :usize) -> &mut T {
        if r < self.row() && c < self.col() {
            &mut self.raw[r][c]
        } else {
            panic!("Index Out of Bounds.");
        }
    }

    pub fn get_row(&self, i :usize) -> &[T; C] { self.raw.index(i) }

    pub fn get_row_mut(&mut self, i :usize) -> &mut [T; C] {
        self.raw.index_mut(i)
    }
}

impl<T, const R: usize, const C: usize> Index<usize> for Matrix<T, R, C> {
    type Output = [T; C];
    fn index(&self, index :usize) -> &[T; C] { &self.raw[index] }
}

impl<T, const R: usize, const C: usize> IndexMut<usize> for Matrix<T, R, C> {
    fn index_mut(&mut self, index :usize) -> &mut [T; C] {
        &mut self.raw[index]
    }
}

impl<T, const R: usize, const C: usize> Index<(usize, usize)>
    for Matrix<T, R, C>
{
    type Output = T;
    fn index(&self, index :(usize, usize)) -> &T { &self.raw[index.0][index.1] }
}

impl<T, const R: usize, const C: usize> IndexMut<(usize, usize)>
    for Matrix<T, R, C>
{
    fn index_mut(&mut self, index :(usize, usize)) -> &mut T {
        &mut self.raw[index.0][index.1]
    }
}

pub fn add<
    T :Default + Copy + Add<Output = T>,
    const R: usize,
    const C: usize,
>(
    A :&Matrix<T, R, C>,
    B :&Matrix<T, R, C>,
) -> Matrix<T, R, C> {
    let mut res = Matrix::<T, R, C>::new();
    for i in 0 .. R {
        for j in 0 .. C {
            res[i][j] = A[i][j] + B[i][j];
        }
    }
    res
}

pub fn sub<
    T :Default + Copy + Sub<Output = T>,
    const R: usize,
    const C: usize,
>(
    A :&Matrix<T, R, C>,
    B :&Matrix<T, R, C>,
) -> Matrix<T, R, C> {
    let mut res = Matrix::<T, R, C>::new();
    for i in 0 .. R {
        for j in 0 .. C {
            res[i][j] = A[i][j] - B[i][j];
        }
    }
    res
}

pub fn mul<
    T :Default + Copy + Add<Output = T> + Mul<Output = T>,
    const R1: usize,
    const C1: usize,
    const C2: usize,
>(
    A :&Matrix<T, R1, C1>,
    B :&Matrix<T, C1, C2>,
) -> Matrix<T, R1, C2> {
    let mut res = Matrix::<T, R1, C2>::new();
    for i in 0 .. R1 {
        for j in 0 .. C2 {
            let mut temp = T::default();
            for c in 0 .. R1 {
                temp = temp + A[i][c] * B[c][j];
            }
            res[i][j] = temp;
        }
    }
    res
}

pub fn tran<
    T :Default + Copy + Add<Output = T> + Mul<Output = T>,
    const R: usize,
    const C: usize,
>(
    A :&Matrix<T, R, C>,
) -> Matrix<T, C, R> {
    let mut res = Matrix::<T, C, R>::new();
    for i in 0 .. R {
        for j in 0 .. C {
            res[i][j] = A[j][i];
        }
    }
    res
}

macro_rules! add {
    ($A:expr, $B:expr) => {
        add(&($A), &($B))
    };
}

macro_rules! sub {
    ($A:expr, $B:expr) => {
        sub(&($A), &($B))
    };
}

macro_rules! mul {
    ($A:expr, $B:expr) => {
        mul(&($A), &($B))
    };
}

macro_rules! tran {
    ($A:expr) => {
        tran(&($A))
    };
}

// impl<T :Display + Debug, const R: usize, const C: usize> Matrix<T, R, C> {
//     pub fn print(&self) {
//         for i in &self.raw {
//             println!("{:?}", i);
//         }
//         print!("\n");
//     }
// }
